package com.kn.keywordmatch.service;

import com.alibaba.fastjson.JSON;
import com.kn.keywordmatch.common.RedisKey;
import com.kn.keywordmatch.dto.req.MatchByDirectionReq;
import com.kn.keywordmatch.dto.req.MatchDirectItem;
import lombok.extern.slf4j.Slf4j;
import org.ahocorasick.trie.Emit;
import org.ahocorasick.trie.Trie;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;

@Component
@Slf4j
public class DirectionKeywordIndex {
    private volatile ConcurrentHashMap<String, Set<String>> invertedIndex;
    private volatile Trie trie;

    @Autowired
    @Qualifier("redisTemplateWithString")
    private RedisTemplate<String,String> redisTemplate;
    public DirectionKeywordIndex() {
        this.invertedIndex = new ConcurrentHashMap<>();
        this.trie = null;  // 初始时 Trie 为空
    }
    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();


    public void buildOrUpdateIndex(List<MatchByDirectionReq> directionReqList) {
        Trie.TrieBuilder trieBuilder = Trie.builder();
        ConcurrentHashMap<String, Set<String>> tempInvertedIndex = new ConcurrentHashMap<>();

        // 全量构建倒排索引
        for (MatchByDirectionReq req : directionReqList) {
            for (MatchDirectItem item : req.getDirectList()) {
                String accountName = item.getAccountName().trim();
                trieBuilder.addKeyword(accountName); // 添加到 Trie 中

                // 更新倒排索引，将 ID 添加到对应关键字的集合
                tempInvertedIndex.computeIfAbsent(accountName, k -> ConcurrentHashMap.newKeySet()).add(req.getPlanID().toString());
            }
        }
        // 完成 Trie 构建
        Trie newTrie = trieBuilder.build();
        updateIndexAndTrie(tempInvertedIndex,newTrie);

    }
    // 每分钟更新一次索引和 Trie
    public void updateIndexAndTrie(ConcurrentHashMap<String, Set<String>> newInvertedIndex, Trie newTrie) {
        try {
            lock.writeLock().lock();
            // 在更新时，新的索引和 Trie 构建完成后再替换
            invertedIndex = newInvertedIndex;
            trie = newTrie;
        }finally {
            lock.writeLock().unlock();
        }

    }

    public void checkAndUpdateTries() {
        if (trie == null) {
            lock.writeLock().lock();
            try {
                if (trie == null) { // 双重检查，确保唯一构建
                    List<MatchByDirectionReq> directionReqList = getRedis(RedisKey.MONITOR_DIRECTION_REDIS_KEY);
                    buildOrUpdateIndex(directionReqList);
                }
            } finally {
                lock.writeLock().unlock();
            }
        }
    }

    public List<Emit> match(String text) {
        if (trie == null) {
            synchronized (this) {
                // 避免多个线程去请求redis
                if (trie == null) {
                    // 请求 redis 并构建 Trie
                    List<MatchByDirectionReq> directionReqList = getRedis(RedisKey.MONITOR_DIRECTION_REDIS_KEY);
                    buildOrUpdateIndex(directionReqList);
                }
            }
        }
        return (List<Emit>) trie.parseText(text);
    }

    private List<MatchByDirectionReq> getRedis(String redisKey) {
        // 从 Redis 中获取整个哈希表
        Map<Object, Object> entries = redisTemplate.opsForHash().entries(redisKey);
        List<MatchByDirectionReq> directionReqList = new ArrayList<>();

        // 遍历条目并转换为 MatchByDirectionReq 对象
        for (Map.Entry<Object, Object> entry : entries.entrySet()) {
            if (entries.isEmpty()) {
                log.error("No data found in Redis for key: {}", redisKey);
                return directionReqList; // 返回空列表或默认值
            }
            String jsonStr = (String) entry.getValue();
            if (jsonStr != null) {
                MatchByDirectionReq directionReq = JSON.parseObject(jsonStr, MatchByDirectionReq.class);
                directionReqList.add(directionReq);
            }
        }

        return directionReqList;
    }

    public Set<String> getPlansByKeyword(String keyword) {
        return invertedIndex.getOrDefault(keyword, Collections.emptySet());
    }

}
